package online.heycm.platform.redis;

import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Cluster;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Pool;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Sentinel;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.StringUtils;
import redis.clients.jedis.JedisPoolConfig;

/**
 * @author heycm
 * @since 2023/11/11 17:02
 */
public class RedisCacheUtil {

    private RedisCacheUtil() {}

    /**
     * Jedis连接池
     */
    public static JedisClientConfiguration createJedisClientConfiguration(RedisProperties redisProperties) {
        Pool poolProperties = redisProperties.getJedis().getPool();
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxIdle(poolProperties.getMaxIdle());
        jedisPoolConfig.setMinIdle(poolProperties.getMinIdle());
        jedisPoolConfig.setMaxTotal(poolProperties.getMaxActive());
        jedisPoolConfig.setMaxWaitMillis(poolProperties.getMaxWait().toMillis());
        jedisPoolConfig.setTestOnBorrow(true);
        jedisPoolConfig.setTestOnReturn(false);
        jedisPoolConfig.setTestWhileIdle(true);
        return JedisClientConfiguration.builder()
                .usePooling().poolConfig(jedisPoolConfig).and().readTimeout(redisProperties.getTimeout())
                .build();
    }

    /**
     * 单点Redis
     */
    public static RedisStandaloneConfiguration createRedisStandaloneConfiguration(RedisProperties redisProperties) {
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
        redisStandaloneConfiguration.setHostName(redisProperties.getHost());
        redisStandaloneConfiguration.setPort(redisProperties.getPort());
        if (StringUtils.hasText(redisProperties.getPassword())) {
            redisStandaloneConfiguration.setPassword(redisProperties.getPassword());
        }
        redisStandaloneConfiguration.setDatabase(redisProperties.getDatabase());
        return redisStandaloneConfiguration;
    }

    /**
     * 哨兵Redis
     */
    public static RedisSentinelConfiguration createRedisSentinelConfiguration(RedisProperties redisProperties) {
        Sentinel sentinel = redisProperties.getSentinel();
        Set<RedisNode> sentinels = RedisCacheUtil.getRedisNodes(sentinel.getNodes());
        RedisSentinelConfiguration redisSentinelConfiguration = new RedisSentinelConfiguration();
        redisSentinelConfiguration.setSentinels(sentinels);
        redisSentinelConfiguration.setMaster(sentinel.getMaster());
        if (StringUtils.hasText(redisProperties.getPassword())) {
            redisSentinelConfiguration.setPassword(redisProperties.getPassword());
        }
        if (StringUtils.hasText(sentinel.getPassword())) {
            redisSentinelConfiguration.setSentinelPassword(sentinel.getPassword());
        }
        redisSentinelConfiguration.setDatabase(redisProperties.getDatabase());
        return redisSentinelConfiguration;
    }

    /**
     * 集群Redis
     */
    public static RedisClusterConfiguration createRedisClusterConfiguration(RedisProperties redisProperties) {
        Cluster cluster = redisProperties.getCluster();
        Set<RedisNode> nodes = RedisCacheUtil.getRedisNodes(cluster.getNodes());
        RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
        redisClusterConfiguration.setClusterNodes(nodes);
        if (StringUtils.hasText(redisProperties.getPassword())) {
            redisClusterConfiguration.setPassword(redisProperties.getPassword());
        }
        redisClusterConfiguration.setMaxRedirects(cluster.getMaxRedirects());
        return redisClusterConfiguration;
    }

    private static Set<RedisNode> getRedisNodes(List<String> nodes) {
        return nodes.stream().map(node -> {
            String[] split = node.split(":");
            return new RedisNode(split[0], Integer.parseInt(split[1]));
        }).collect(Collectors.toSet());
    }

    /**
     * RedisTemplate
     */
    public static RedisTemplate<String, Object> createRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        StringRedisSerializer keySerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer<Object> valueSerializer = new Jackson2JsonRedisSerializer<>(Object.class);


        ObjectMapper objectMapper = new ObjectMapper();
        // 值为null字段不转换
        objectMapper.setSerializationInclusion(Include.NON_NULL);
        // 统一日期格式
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        // 关闭默认转换timestamps格式
        objectMapper.configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS, false);
        // 忽略空bean转json错误
        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        // 忽略json存在但Java对象中不存在的字段
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        valueSerializer.setObjectMapper(objectMapper);

        redisTemplate.setKeySerializer(keySerializer);
        redisTemplate.setHashKeySerializer(keySerializer);
        redisTemplate.setValueSerializer(valueSerializer);
        redisTemplate.setDefaultSerializer(valueSerializer);
        return redisTemplate;
    }

    public static String namespace(String namespace, String key) {
        return StringUtils.hasText(namespace) ? namespace + ":" + key : key;
    }

    public static String[] namespace(String namespace, String... keys) {
        String[] strings = new String[keys.length];
        for (int i = 0; i < keys.length; i++) {
            strings[i] = namespace(namespace, keys[i]);
        }
        return strings;
    }
}
